package be.nvlooy.moviereminder.service.implementations;

import java.util.GregorianCalendar;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import be.nvlooy.moviereminder.entities.Friendship;
import be.nvlooy.moviereminder.entities.FriendshipStatus;
import be.nvlooy.moviereminder.entities.User;
import be.nvlooy.moviereminder.exception.ServiceException;
import be.nvlooy.moviereminder.service.BasicService;
import be.nvlooy.moviereminder.service.FriendshipService;

@Service
@Scope("singleton")
public class FriendshipServiceImpl extends BasicService implements FriendshipService {
	
	private EntityManager em;
	
	@Autowired
	public void setEntityManager(EntityManager em) {
		this.em = em;
	}
	
	@Override
	public Friendship getFriendshipByID(int friendshipID) {
		Friendship friendship = null;
		
		try {
			friendship = em.find(Friendship.class, (long) friendshipID);
		}
		catch(javax.persistence.NoResultException ex) {}
		
		return friendship;
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public Friendship getFriendship(User user, User friend) {
		Friendship friendship = null;
		
		Query query = em.createNamedQuery("Friendship.getByUserAndFriend");
		query.setParameter("user", user);
		query.setParameter("friend", friend);
		
		try {
			friendship = (Friendship) query.getSingleResult();
		}
		catch(javax.persistence.NoResultException ex) {}
		
		return friendship;
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Friendship> findByName(User user, String search, FriendshipStatus status) {
		if(status == null)
			status = FriendshipStatus.ACTIVE;
			
		List<Friendship> friends = null;
		
		Query query = em.createNamedQuery("Friendship.findByName");
		query.setParameter("user", user);
		query.setParameter("search", "%"+search+"%");
		query.setParameter("status", status);
		
		try {
			friends = query.getResultList();
		}
		catch(javax.persistence.NoResultException ex) {}
		
		return friends;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public List<Friendship> getFriendships(User user, FriendshipStatus status) {
		List<Friendship> friends = null;
		Query query;
		
		// pending friendship can be a requesting or a givven friendship
		if(status == FriendshipStatus.PENDING) 
			query = em.createNamedQuery("Friendship.getOutgoingRequests");
		else {
			query = em.createNamedQuery("Friendship.getByUserWithStatus");
			query.setParameter("status", status);
		}
		
		query.setParameter("user", user);
		
		try {
			friends = query.getResultList();
		}
		catch(javax.persistence.NoResultException ex) {}
		
		return friends;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public List<Friendship> getIncomingRequests(User user) {
		List<Friendship> friends = null;
		Query query;

		query = em.createNamedQuery("Friendship.getIncomingRequests");
		query.setParameter("user", user);
		
		try {
			friends = query.getResultList();
		}
		catch(javax.persistence.NoResultException ex) {}
		
		return friends;
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public Friendship requestFriendship(User user, User friend) {
		if(user.getId() == friend.getId())
			throw new ServiceException("friendshipCantAddSelf");
		
		// check if the friendship already exists
		Friendship existing = getFriendship(user, friend);
		
		if(existing != null) {
			if(existing.getStatus() == FriendshipStatus.BLOCKED)
				throw new ServiceException("friendshipIsBlocked");
			else if(existing.getStatus() == FriendshipStatus.PENDING)
				throw new ServiceException("friendshipRequestPending");
			else if(existing.getStatus() == FriendshipStatus.ACTIVE)
				throw new ServiceException("friendshipAlreadyActive");
			else
				throw new ServiceException("friendshipRequestException");
		}
		
		// check if the friend has an active request or relation with the current user
		Friendship frndExisting = getFriendship(friend, user);
		if(frndExisting != null) {
			// hase he blocked current user?
			if(frndExisting.getStatus() == FriendshipStatus.BLOCKED)
				throw new ServiceException("friendshipIsBlockedByFriend");
			else
				throw new ServiceException("friendshipFriendAlreadyAddedYou");
		}
		
		// create a newly "pending" relationship
		Friendship friendship = new Friendship();
		friendship.setUser(user);
		friendship.setFriend(friend);
		friendship.setStatus(FriendshipStatus.PENDING);
		
		try {
			em.persist(friendship);
		}
		catch (RuntimeException e) {
			throw new ServiceException("friendshipRequestException", e);
		}
		
		return friendship;
	}
	
	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public Friendship confirmFriendship(User user, Friendship friendshipReq) {
		if(friendshipReq.getFriend().getId() != user.getId())
			throw new RuntimeException("friendshipNotReceiver");
		
		friendshipReq.setStatus(FriendshipStatus.ACTIVE);
		
		// create new friendship record for the friend's side!
		Friendship newFriendShip = new Friendship();
		newFriendShip.setUser(friendshipReq.getFriend());
		newFriendShip.setFriend(friendshipReq.getUser());
		newFriendShip.setStatus(FriendshipStatus.ACTIVE);
		
		try {
			em.merge(friendshipReq);
			em.persist(newFriendShip);
		}
		catch (RuntimeException e) {
			throw new ServiceException("friendshipConfirmFail", e);
		}
		
		return newFriendShip;
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public Friendship updateFriendship(Friendship friendship) {
		
		try {
			em.merge(friendship);
		}
		catch(RuntimeException e) {
			throw new ServiceException("friendshipUpdateError", e);
		}
		
		return friendship;
	}
	
	@Override
	public boolean isFriendWith(User user, User friend) {
		return getFriendship(user, friend) != null;
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public void cancelFriendship(User user, User friend) {
		// remove friendship from current user
		Friendship friendship = getFriendship(user, friend);
		
		if(friendship == null)
			throw new ServiceException("friendshipDoesNotExist");
		
		try {
			em.remove(friendship);
			
			// if firnd has accepted this friendship => remove!
			Friendship frndsFriendship = getFriendship(friend, user);
			
			if(frndsFriendship != null)
				em.remove(frndsFriendship);
		}
		catch(ServiceException e) {}
		catch(RuntimeException e) {
			throw new ServiceException("friendshipCancelError", e);
		}
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public void blockFriendship(Friendship friendship, boolean block) {
		if(friendship == null)
			throw new ServiceException("parameterException");
		
		if(friendship.getStatus() == FriendshipStatus.PENDING)
			throw new ServiceException("friendshipBlockFailCausePending");
		
		try {
			if(block)
				friendship.setStatus(FriendshipStatus.BLOCKED);
			else
				friendship.setStatus(FriendshipStatus.ACTIVE);
			
			em.merge(friendship);
		}
		catch(RuntimeException e) {
			throw new ServiceException("persistenceException", e);
		}
	}
	
}
